package com.skynet.supervision.common;

import cn.hutool.core.text.StrFormatter;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.skynet.supervision.exceptions.CheckedException;
import com.skynet.supervision.utils.Strings;
import lombok.*;

import javax.annotation.Nonnull;
import javax.validation.constraints.NotNull;
import java.io.Serial;
import java.io.Serializable;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

@EqualsAndHashCode
@ToString
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@SuppressWarnings("unused")
public class ResultGeneric<T> implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;

    /**
     * 错误码：0成功，其他失败
     */
    @Builder.Default
    protected int code = 0;

    /**
     * 错误信息
     */
    @Builder.Default
    protected String message = "Success";

    @JsonInclude(JsonInclude.Include.NON_NULL)
    protected T data;

    public static <T> ResultGeneric<T> newResult(int code, String message, T data) {
        return new ResultGeneric<>(code, message, data);
    }

    public static <T> ResultGeneric<T> newResult(int code, String message) {
        return newResult(code, message, null);
    }

    public static <T> ResultGeneric<T> newResult() {
        return newResult(ErrCode.OK.getCode(), ErrCode.OK.getMessage(), null);
    }

    public static <T> ResultGeneric<T> newResult(ErrCode code, String message, T data) {
        return newResult(code.getCode(), message, data);
    }

    public static <T> ResultGeneric<T> newResult(ErrCode code, T data) {
        return newResult(code.getCode(), code.getMessage(), data);
    }

    public static <T> ResultGeneric<T> newResult(ErrCode code, String message) {
        return newResult(code.getCode(), message, null);
    }

    public static <T> ResultGeneric<T> newResult(ErrCode code) {
        return newResult(code.getCode(), code.getMessage(), null);
    }

    public static <T> ResultGeneric<T> success(String message, T data) {
        return newResult(ErrCode.OK.getCode(), message, data);
    }

    public static <T> ResultGeneric<T> success() {
        return success(ErrCode.OK.getMessage(), null);
    }

    public static <T> ResultGeneric<T> success(T data) {
        return success(ErrCode.OK.getMessage(), data);
    }

    public static <T> ResultGeneric<T> failure(int code, String message) {
        return newResult(code, message);
    }

    public static <T> ResultGeneric<T> failure(int code, String fmt, Object... args) {
        return failure(code, Strings.sprintf(fmt, args));
    }

    public static <T> ResultGeneric<T> failure(ErrCode code, String message) {
        return failure(code.getCode(), message);
    }

    public static <T> ResultGeneric<T> failure(ErrCode code, String fmt, Object... args) {
        return failure(code, Strings.sprintf(fmt, args));
    }

    public static <T> ResultGeneric<T> failure(int code) {
        return failure(code, "请求失败");
    }

    public static <T> ResultGeneric<T> failure(ErrCode code) {
        return failure(code.getCode(), code.getMessage());
    }


    public static <T> ResultGeneric<T> failure(String message) {
        return failure(ErrCode.NORMAL_ERROR, message);
    }

    public static <T> ResultGeneric<T> failure() {
        return failure(ErrCode.NORMAL_ERROR);
    }

    public static <T> ResultGeneric<T> failure(String fmt, Object... args) {
        return failure(ErrCode.NORMAL_ERROR, fmt, args);
    }

    public ResultGeneric<T> withCode(int code) {
        this.code = code;
        return this;
    }

    public ResultGeneric<T> withMessage(String message) {
        this.message = message;
        return this;
    }

    public ResultGeneric<T> wrapMessage(String externalMessage) {
        this.message = StrFormatter.format("{}: caused by {}", externalMessage, message);
        return this;
    }

    public ResultGeneric<T> withData(T data) {
        this.data = data;
        return this;
    }

    public CheckedException wrap() {
        return CheckedException.wrap(this);
    }

    public boolean hasError() {
        return !ok();
    }

    public boolean ok() {
        return ok(e -> e == 0);
    }

    public boolean ok(Function<Integer, Boolean> func) {
        return func.apply(code);
    }


    @FunctionalInterface
    public interface FailedCallBack {
        void invoke(int code, String message);
    }

    public void ifFailed(@Nonnull FailedCallBack consumer) {
        if (!ok()) {
            consumer.invoke(code, message);
        }
    }

    public T ifSuccess(@Nonnull Consumer<T> consumer) {
        if (ok()) {
            consumer.accept(data);
            return data;
        }
        return null;
    }

    public void computeIf(
            @NotNull Predicate<ResultGeneric<T>> predicate,
            @NotNull Consumer<ResultGeneric<T>> consumer) {
        if (predicate.test(this)) {
            consumer.accept(this);
        }
    }

    public void computeIfOk(@NotNull Consumer<ResultGeneric<T>> consumer) {
        if (ok()) {
            consumer.accept(this);
        }
    }

    public T orElseThrow(@NotNull Supplier<? extends Throwable> exceptionSupplier) throws Throwable {
        if (ok()) {
            return data;
        }
        throw exceptionSupplier.get();
    }

    public T orElseThrow() throws Throwable {
        return orElseThrow(() -> Result.wrapGeneric(this).wrap());
    }

    public static <T> ResultGeneric<T> notImplementGeneric() {
        val stackElement = new Throwable().getStackTrace()[1];
        val invokeMethodName = stackElement.getMethodName();
        return ResultGeneric.<T>builder()
                .code(-4783)
                .message(StrFormatter.format("该函数:{}尚未实现", invokeMethodName))
                .build();
    }
}
